package com.bangcommunity.bbframe.sdm.web.exception;

import com.bangcommunity.bbframe.common.base.BaseResult;
import com.bangcommunity.bbframe.common.base.ResultCode;
import com.bangcommunity.bbframe.common.exception.BaseException;
import com.bangcommunity.bbframe.common.exception.BaseRtmException;
import com.bangcommunity.bbframe.common.exception.ExceptionUtil;
import com.bangcommunity.bbframe.common.utils.lang.StringUtils;
import com.bangcommunity.bbframe.sdm.utils.BeanValidators;
import com.bangcommunity.bbframe.sdm.utils.JsonMapper;
import com.bangcommunity.bbframe.sdm.utils.MediaTypes;
import com.bangcommunity.bbframe.sdm.utils.RequestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.servlet.view.UrlBasedViewResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;

/**
 * 自定义ExceptionHandler，
 * 
 * @author tanghc
 */
// 会被Spring-MVC自动扫描，但又不属于Controller的annotation。
// @ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
    private static Logger logger = LoggerFactory.getLogger(RestExceptionHandler.class);

    /**
     * 处理JSR311 Validation异常.
     */
    @ExceptionHandler(value = { ConstraintViolationException.class })
    public final Object handleException(ConstraintViolationException ex, WebRequest request,
            HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
        Map<String, String> errors = BeanValidators.extractPropertyAndMessage(ex.getConstraintViolations());
        String errorMsg = null;
        if (null != errors) {
            for (Map.Entry<String, String> entry : errors.entrySet()) {
                errorMsg = entry.getValue();
                if (StringUtils.isNotBlank(errorMsg)) {
                    break;
                }
            }
        }
        return processException(ex, ResultCode.OTHER.getCode(), errorMsg, request, httpRequest, httpResponse);
    }

    /**
     * 处理BaseCustomRtmException异常.
     */
    @ExceptionHandler(value = { BaseRtmException.class })
    public final Object handleBaseCustomRtmException(BaseRtmException ex, WebRequest request,
            HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
        return processException(ex, ResultCode.OTHER.getCode(), ex.getMessage(), request, httpRequest, httpResponse);
    }

    /**
     * 处理BaseCustomRtmException异常.
     */
    @ExceptionHandler(value = { BaseException.class })
    public final Object handleBaseException(BaseException ex, WebRequest request, HttpServletRequest httpRequest,
            HttpServletResponse httpResponse) {
        return processException(ex, ResultCode.OTHER.getCode(), ex.getMessage(), request, httpRequest, httpResponse);
    }

    /**
     * 处理所有未分类型的异常.
     */
    @ExceptionHandler(value = { Exception.class })
    public final Object handleException1(Exception ex, WebRequest request, HttpServletRequest httpRequest,
            HttpServletResponse httpResponse) {
        String stackTraceStr = "请求失败";
        return processException(ex, ResultCode.OTHER.getCode(), stackTraceStr, request, httpRequest, httpResponse);

    }

    /**
     * 处理所有未分类型的异常.
     */
    @ExceptionHandler(value = { Throwable.class })
    public final Object handleException1(Throwable ex, WebRequest request, HttpServletRequest httpRequest,
            HttpServletResponse httpResponse) {
        String stackTraceStr = "请求失败";
        return processException(new RuntimeException("系统异常", ex), ResultCode.OTHER.getCode(), stackTraceStr, request,
                httpRequest, httpResponse);

    }

    // /**
    // * 处理所有未分类型的异常.
    // */
    // @ExceptionHandler(value = { UnauthorizedException.class })
    // public final Object handleException2(UnauthorizedException ex, WebRequest request,
    // HttpServletRequest httpRequest,
    // HttpServletResponse httpResponse) {
    // String stackTraceStr = "没有权限访问";
    // return processException(ex, ResultCode.OTHER.getCode(), stackTraceStr, request, httpRequest,
    // httpResponse);
    //
    // }

    /**
     * 处理上传文件过大异常
     */
    @ExceptionHandler(value = { MaxUploadSizeExceededException.class })
    public final Object maxSizeException(MaxUploadSizeExceededException ex, WebRequest request,
            HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
        String stackTraceStr = "文件上传不能超过" + this.getFileMB(ex.getMaxUploadSize());
        return processException(ex, ResultCode.OTHER.getCode(), stackTraceStr, request, httpRequest, httpResponse);
    }

    /**
     * 实际处理异常如何响应到客户端,子类可以重写自定义响应。 默认的响应是将异常转成BaseResult json输出
     * 
     * @param e
     * @param statusCode
     * @param message
     * @param request
     * @param httpRequest
     * @param httpResponse
     * @return
     */
    protected Object processException(Exception e, String statusCode, String message, WebRequest request,
            HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
        Throwable cause = ExceptionUtil.getCause(e, BaseRtmException.class, ConstraintViolationException.class);
        if (null != cause) {
            logger.warn("controller exception: requestUri={},msg={}", httpRequest.getRequestURI(), message, e);
        }
        logger.warn("controller exception: requestUri={},msg={}", httpRequest.getRequestURI(), message);
        if (!RestConf.isRest) {
            boolean ajaxRequest = RequestUtils.isAjaxRequest(httpRequest);
            if (!ajaxRequest) {
                logger.error("error redirect to 500.jsp");
                Map<String, Object> model = new HashMap<>();
                model.put("message", StringUtils.isNotBlank(message) ? message : "操作失败");
                return new ModelAndView(UrlBasedViewResolver.FORWARD_URL_PREFIX + "/common/error/500.jsp", model);
            }
        }
        String rbJson = JsonMapper.getInstance().toJson(BaseResult.fail(message));
        HttpHeaders headers = new HttpHeaders();
        // headers.setContentType(MediaType.parseMediaType(MediaTypes.TEXT_PLAIN_UTF_8));
        headers.setContentType(MediaType.parseMediaType(MediaTypes.JSON_UTF_8));
        logger.warn("controller exception: requestUri={},msg={},result={}", httpRequest.getRequestURI(), e.getMessage(),
                rbJson);
        return this.handleExceptionInternal(e, rbJson, headers, HttpStatus.OK, request);
    }

    /**
     * 计算文件MB大小
     *
     * @param byteFile
     *            字节数
     * @return
     */
    private String getFileMB(long byteFile) {
        if (byteFile == 0) {
            return "0MB";
        }
        long mb = 1024 * 1024;
        return "" + byteFile / mb + "MB";
    }
}
